/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package org.ruslan.kolobok;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.ruslan.kolobok.model.Barrel;
import org.ruslan.kolobok.model.Fx;
import org.ruslan.kolobok.model.IKillable;
import org.ruslan.kolobok.model.LevelObject;
import org.ruslan.kolobok.model.Projectile;
import org.ruslan.kolobok.model.Soldier;
import org.ruslan.kolobok.model.Type;

import com.badlogic.gdx.math.Vector3;

public class World {

	public static final int WORLD_STATE_RUNNING = 0;
	public static final int WORLD_STATE_NEXT_LEVEL = 1;
	public static final int WORLD_STATE_GAME_OVER = 2;
	// public static final Vector3 gravity = new Vector3(0, -1, 0);
	private static float WORLD_GRAVITY = -1000;
	public static float VANISHING_LINE = 1500;

	// public final Random rand;

	public int score;
	public int state;

	public final List<Soldier> soldiers;
	public final List<LevelObject<Type>> hittables;
	public final List<Barrel> barrels;
	public final List<Projectile> projectiles;
	public final List<Fx> effects;
	private Level currentLevel;

	public float playerX;
	public float playerY;

	public float maxX = 1000;
	public float width;

	// public Ray pickingRay;

	public World() {
		// rand = new Random();
		// soldiers = new LinkedList<Soldier>();
		// generateLevel();
		currentLevel = new Level();
		soldiers = currentLevel.getSoldiers();
		hittables = currentLevel.getHittables();
		barrels = currentLevel.getBarrels();
		projectiles = new LinkedList<Projectile>();
		effects = new LinkedList<Fx>();
		width = currentLevel.getWidth();
		playerX = currentLevel.getWidth() / 2;

		this.score = 0;
		this.state = WORLD_STATE_RUNNING;
	}

	public void update(float deltaTime) {
		deltaTime = deltaTime / 5;
		for (int i = 0; i < 5; i++) {

			synchronized (World.class) {

				// move soldiers
				for (Iterator<Soldier> iterator = soldiers.iterator(); iterator.hasNext();) {
					Soldier soldier = iterator.next();
					if (soldier.isActive()) {
						Vector3 velocity = soldier.getVelocity();
						if (!velocity.isZero()) {
							soldier.getPosition().x += velocity.x * deltaTime;
							soldier.getPosition().y += velocity.y * deltaTime;
							soldier.getPosition().z += velocity.z * deltaTime;
						}
						if (soldier.getPosition().x < playerX - VANISHING_LINE) {
							soldier.getPosition().x += VANISHING_LINE * 2;
						}

						soldier.update(deltaTime);
					}
					// else {
					// iterator.remove();
					// }
				}

				// check hits
				if (!projectiles.isEmpty()) {
					for (Iterator<LevelObject<Type>> iterator = hittables.iterator(); iterator.hasNext();) {
						LevelObject<Type> soldier = iterator.next();
						if (soldier.isActive()) {

							for (Iterator<Projectile> iteratorProjectile = projectiles.iterator(); iteratorProjectile
									.hasNext();) {
								Projectile projectile = iteratorProjectile.next();
								Vector3 hitPoint = hit(projectile, soldier, deltaTime);
								if (hitPoint != null) {
									if (soldier instanceof IKillable) {
										IKillable killable = (IKillable) soldier;
										killable.setHealth(killable.getHealth() - 1);
									}
									// soldier.setState(SoldierState.DIE);
									addNewFx(hitPoint, Assets.hitType);
									iteratorProjectile.remove();
									break;
								}
							}
						}
					}
				}

				// update projectiles
				if (!projectiles.isEmpty()) {
					for (Iterator<Projectile> iterator = projectiles.iterator(); iterator.hasNext();) {
						Projectile projectile = iterator.next();
						float newY = projectile.getPosition().y + projectile.getVelocity().y * deltaTime;
						if (newY > 0) {
							// update position
							projectile.getPosition().add(projectile.getVelocity().x * deltaTime,
									projectile.getVelocity().y * deltaTime, projectile.getVelocity().z * deltaTime);
							// apply gravity
							projectile.getVelocity().y += WORLD_GRAVITY * deltaTime;
						} else {
							float ratio = Math.abs(0 - projectile.getPosition().y)
									/ Math.abs(projectile.getPosition().y - newY);
							float x = projectile.getPosition().x + projectile.getVelocity().x * deltaTime * ratio;
							float y = projectile.getPosition().y + projectile.getVelocity().y * deltaTime * ratio;
							float z = projectile.getPosition().z + projectile.getVelocity().z * deltaTime * ratio;

							addNewFx(new Vector3(x, y, z), Assets.splashType);
							System.out.println("removed");
							iterator.remove();
						}
					}
				}

				for (Iterator<Fx> iterator = effects.iterator(); iterator.hasNext();) {
					Fx fx = iterator.next();
					fx.setStateTime(fx.getStateTime() + deltaTime);
					if (fx.getStateTime() > fx.getDuration()) {
						iterator.remove();
					}
				}

				playerX += deltaTime * 30; // move 30sm/s

				boolean noActiveEnemies = true;
				for (Soldier soldier : soldiers) {
					if (soldier.isActive()) {
						noActiveEnemies = false;
						break;
					}
				}

				if (noActiveEnemies) {
					state = World.WORLD_STATE_NEXT_LEVEL;
				}
			}
		}
	}

	private void addNewFx(Vector3 hitPoint, Type type) {
		Fx fx = new Fx(type);
		fx.setStateTime(0);
		fx.getPosition().set(hitPoint);
		effects.add(fx);
	}

	private Vector3 hit(Projectile projectile, LevelObject<Type> soldier, float deltaTime) {
		if (projectile.getPosition().z > soldier.getPosition().z) {
			float newZ = projectile.getPosition().z + projectile.getVelocity().z * deltaTime;
			if (newZ < soldier.getPosition().z) {
				float ratio = Math.abs(soldier.getPosition().z - projectile.getPosition().z)
						/ Math.abs(projectile.getPosition().z - newZ);
				float x = projectile.getPosition().x + projectile.getVelocity().x * deltaTime * ratio;
				float y = projectile.getPosition().y + projectile.getVelocity().y * deltaTime * ratio;
				// System.out.println("crossed");
				if (OverlapTester.pointInRectangle(soldier.getPosition().x - soldier.getWidth() / 2,
						soldier.getPosition().y, soldier.getWidth(), soldier.getHeight(), x, y)) {
					float z = soldier.getPosition().z + 1;// projectile.getPosition().z
															// +
															// projectile.getVelocity().z
															// * deltaTime *
															// ratio;
					return new Vector3(x, y, z);
				}
			}
		}

		return null;
	}
}
